package navigation;

import config.*;

/**
 *
 * @author hyurah
 */

public abstract class Navigator{

	protected Map map;
	protected int facingDirection;
	protected int xPosition;
	protected int yPosition;
	protected int xStart;
	protected int yStart;

	public Navigator() {
		map=new Map();
		facingDirection=Direction.SOUTH;
		xPosition=1;
		yPosition=0;
		xStart=-1;
		yStart=0;
		map.deadEndFound(xPosition, yPosition, facingDirection);
	}

	public Navigator(Navigator oldNavigator) {
		map=oldNavigator.map;
		facingDirection=oldNavigator.facingDirection;
		xPosition=oldNavigator.xPosition;
		yPosition=oldNavigator.yPosition;
		xStart=oldNavigator.xStart;
		yStart=oldNavigator.yStart;
	}

	public void resetToStartingPosition() {
		if(xStart<0) xPosition=1;
		else xPosition=xStart;
		yPosition=yStart;
	}

	public boolean fieldIsKnown() {
		return map.fieldIsKnown(xPosition, yPosition);
	}

	public boolean fieldIsExit() {
		return map.fieldIsExit(xPosition, yPosition);
	}

	public void directionChanged(int direction) {
		facingDirection=(facingDirection+direction)%4;
	}

	public void moveOneStep() throws IllegalMoveException {
		switch(facingDirection) {
			case Direction.NORTH:
				if(yPosition-1<0) throw new IllegalMoveException(xPosition, yPosition-1);
				yPosition--;
				break;
			case Direction.EAST:
				if(xPosition+1>=map.getXSize()) throw new IllegalMoveException(xPosition+1, yPosition);
				xPosition++;
				break;
			case Direction.SOUTH:
				if(yPosition+1>=map.getYSize()) throw new IllegalMoveException(xPosition, yPosition+1);
				yPosition++;
				break;
			case Direction.WEST:
				if(xPosition-1<0) throw new IllegalMoveException(xPosition-1, yPosition);
				xPosition--;
				break;
		}
	}

	public void addField(boolean forward, boolean right, boolean backward, boolean left) throws IllegalMapPositionException {
		if(xStart<0) {
			if(Config.FIRSTCURVEPOINTSTOMAZE) {
				if(left) {
					xStart=1;
					map.startKnown(Direction.WEST);
				} else if(right) {
					map.switchStartingLine();
					xStart=map.getXSize()-2;
					xPosition=map.getXSize()-2;
					map.startKnown(Direction.EAST);
				}
			} else {
				checkStartingLine();
			}
		}
		if(xPosition<1 || yPosition<1 || xPosition>map.getXSize()-2 || yPosition>map.getYSize()-2)
			throw new IllegalMapPositionException(xPosition, yPosition);
		switch(facingDirection) {
			case Direction.NORTH:
				map.addField(xPosition, yPosition, forward, right, backward, left);
				break;
			case Direction.EAST:
				map.addField(xPosition, yPosition, left, forward, right, backward);
				break;
			case Direction.SOUTH:
				map.addField(xPosition, yPosition, backward, left, forward, right);
				break;
			case Direction.WEST:
				map.addField(xPosition, yPosition, right, backward, left, forward);
				break;
		}
		if(!map.measuresKnown()) {
			checkMeasures(xPosition, yPosition);
		}
	}

	public void objectFound(int direction) {
		if(map.fieldIsKnown(xPosition, yPosition))
			map.objectFound(xPosition, yPosition, (facingDirection+direction)%4);
	}

	public void deadEndFound() {
		map.deadEndFound(xPosition, yPosition, (facingDirection+2)%4);
	}

	public void redPointFound() {
		map.redPointFound(xPosition, yPosition);
	}

	public String toString() {
		String direction = "";
		switch(facingDirection) {
			case Direction.NORTH: direction="North"; break;
			case Direction.EAST: direction="East"; break;
			case Direction.SOUTH: direction="South"; break;
			case Direction.WEST: direction="West"; break;
		}
		return "X:" + xPosition + " Y:" + yPosition + " Direction:" + direction + "\n"
				+ getLocalFieldDirectionState(0) + ", " + getLocalFieldDirectionState(1) + ", " + getLocalFieldDirectionState(2) + ", " + getLocalFieldDirectionState(3);
	}

	public String mapToString() {
		return map.toString();
	}

	protected FieldDirectionState getLocalFieldDirectionState(int direction) {
		return map.getFieldDirectionState(xPosition, yPosition, direction);
	}

	protected boolean isPassable(int direction) {
		return getLocalFieldDirectionState(direction)==FieldDirectionState.PATH;
	}

	private void checkStartingLine() {
		if(xPosition<1) {
			map.switchStartingLine();
			xStart=map.getXSize()-2;
			xPosition=map.getXSize()-3;
			map.startKnown(Direction.EAST);
		} else if(xPosition>1) {
			xStart=1;
			map.startKnown(Direction.WEST);
		}
	}

	private void checkMeasures(int x, int y) {
		int startX=0, endX=0, startY=0, endY=0;
		for(int i=0; i<Config.MAXFIELDS; i++) {
			if(map.fieldIsKnown(i, y)) startX=i;
			if(map.fieldIsKnown(x, i)) startY=i;
		}
		for(int i=Config.MAXFIELDS-1; i>=0; i--) {
			if(map.fieldIsKnown(i, y)) endX=i;
			if(map.fieldIsKnown(x, i)) endY=i;
		}
		if(endX-startX>Config.MINFIELDS-1) map.shrinkY();
		else if(endY-startY>Config.MINFIELDS-1)  {
			if(xStart!=0) {
				xStart-=Config.MAXFIELDS-Config.MINFIELDS;
				xPosition-=Config.MAXFIELDS-Config.MINFIELDS;
				map.shrinkX(false);
			} else map.shrinkX(true);
		}
	}

	abstract public int decideDirection() throws IllegalMapStateException;

}
